﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;

using OpenSim.Data;
using OpenSim.Data.MySQL;
using OpenSim.Data.MSSQL;

using OpenSim.Framework;
using OpenSim.AssetLoader;
using OpenMetaverse;

namespace InventoryServer
{
    public class DatabaseMigration
    {
        private IAssetDataPlugin AssetDatabaseConnection;
        private IInventoryDataPlugin InventoryDatabaseConnection;
        private string invConnectionString;
        private string assetConnectionString;
        private string defaultAssetsFile;
        private List<AssetBase> defaultAssets;

        private UUID worldLibOwner = new UUID("11111111-1111-0000-0000-000100bba000");

        public DatabaseMigration(string databaseType, string invConnString, string assetConnString, string defaultAssetsFilePath)
        {

            invConnectionString = invConnString;
            assetConnectionString = assetConnString;
            defaultAssetsFile = defaultAssetsFilePath;
            Logger.Info("[DatabaseMigration] Connection strings and deafault assets description file set from config");
            defaultAssets = new List<AssetBase>();

            if (databaseType == "mssql")
            {
                InventoryDatabaseConnection = new MSSQLInventoryData();
                AssetDatabaseConnection = new MSSQLAssetData();
            }
            else if (databaseType == "mysql")
            {
                InventoryDatabaseConnection = new MySQLInventoryData();
                AssetDatabaseConnection = new MySQLAssetData();
            }
        }

        public bool MigrateInventoryStoreTables()
        {
            try
            {
                InventoryDatabaseConnection.Initialise(invConnectionString);
                Logger.Info("[DatabaseMigration] InventoryStore migration succesfull");
                return true;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("[DatabaseMigration] Could not migrate inventory database with connection string {0}", invConnectionString);
                return false;
            }
        }

        public void CreateWorldLibrary(string worldLibraryID)
        {
            UUID rootID;
            if (UUID.TryParse(worldLibraryID, out rootID))
            {
                InventoryFolderBase rootFolderBase = InventoryDatabaseConnection.getInventoryFolder(rootID);
                if (rootFolderBase == null)
                {
                    Logger.InfoFormat("[WorldLibrary] Could not find root folder with ID {0}", rootID.ToString());
                    // Make rootfolder
                    rootFolderBase = new InventoryFolderBase();
                    rootFolderBase.ID = rootID;
                    rootFolderBase.Name = "OpenSim Library";
                    rootFolderBase.Owner = worldLibOwner;
                    rootFolderBase.ParentID = UUID.Zero;
                    rootFolderBase.Type = (short)CableBeachMessages.InventoryType.Folder;
                    rootFolderBase.Version = 1;

                    // Sort default assets
                    Hashtable AssetFolders = SortDefaultAssetsToFolders();
                    // Create root folder
                    InventoryDatabaseConnection.addInventoryFolder(rootFolderBase);
                    Logger.Info("[WorldLibrary] Created root folder");
                    // Add default assets to subfolder
                    CreateWorldLibraryFolders(AssetFolders, rootFolderBase.ID);
                }
                else
                {
                    // Updating still needs work...
                    List<InventoryFolderBase> foundFolders = InventoryDatabaseConnection.getInventoryFolders(rootFolderBase.ID);
                    Logger.InfoFormat("[WorldLibrary] World Library already created and it has {0} folders", foundFolders.Count.ToString());
                    //Hashtable AssetFolders = SortDefaultAssetsToFolders();
                    //bool found;
                    //foreach (DictionaryEntry SubFolder in AssetFolders)
                    //{
                    //    found = false;
                    //    foreach (InventoryFolderBase BaseItem in foundFolders)
                    //    {
                    //        if (BaseItem.Name == SubFolder.Key.ToString())
                    //            // Go and update this folder with SubFolder.Value:s assets!
                    //            found = true;
                    //    }
                    //}
                }
            }

        }

        private Hashtable SortDefaultAssetsToFolders()
        {
            List<AssetBase> textureAssets = new List<AssetBase>();
            List<AssetBase> meshAssets = new List<AssetBase>();
            List<AssetBase> materialAssets = new List<AssetBase>();
            List<AssetBase> particleAssets = new List<AssetBase>();
            List<AssetBase> soundAssets = new List<AssetBase>();
            List<AssetBase> scriptAssets = new List<AssetBase>();
            List<AssetBase> gestureAssets = new List<AssetBase>();
            List<AssetBase> clothingAssets = new List<AssetBase>();
            List<AssetBase> bodypartAssets = new List<AssetBase>();
            List<AssetBase> animationAssets = new List<AssetBase>();

            foreach (AssetBase asset in defaultAssets)
            {
                if (asset.Type == (sbyte)CableBeachMessages.AssetType.Texture)
                    textureAssets.Add(asset);
                else if (asset.Type == (sbyte)CableBeachMessages.AssetType.OgreMesh)
                    meshAssets.Add(asset);
                else if (asset.Type == (sbyte)CableBeachMessages.AssetType.OgreMaterial)
                    materialAssets.Add(asset);
                else if (asset.Type == (sbyte)CableBeachMessages.AssetType.OgreParticle)
                    particleAssets.Add(asset);
                else if (asset.Type == (sbyte)CableBeachMessages.AssetType.Sound)
                    soundAssets.Add(asset);
                else if (asset.Type == (sbyte)CableBeachMessages.AssetType.LSLText)
                    scriptAssets.Add(asset);
                else if (asset.Type == (sbyte)CableBeachMessages.AssetType.Gesture)
                    gestureAssets.Add(asset);
                else if (asset.Type == (sbyte)CableBeachMessages.AssetType.Clothing)
                    clothingAssets.Add(asset);
                else if (asset.Type == (sbyte)CableBeachMessages.AssetType.Bodypart)
                    bodypartAssets.Add(asset);
                else if (asset.Type == (sbyte)CableBeachMessages.AssetType.Animation)
                    animationAssets.Add(asset);
            }
            Hashtable returnTable = new Hashtable();
            returnTable["Textures"] = textureAssets;
            returnTable["3D Meshes"] = meshAssets;
            returnTable["3D Materials"] = materialAssets;
            returnTable["3D Particles"] = particleAssets;
            returnTable["Sounds"] = soundAssets;
            returnTable["Scripts"] = scriptAssets;
            returnTable["Gestures"] = gestureAssets;
            returnTable["Clothing"] = clothingAssets;
            returnTable["Bodyparts"] = bodypartAssets;
            returnTable["Animations"] = animationAssets;

            return returnTable;
        }
        

        private void CreateWorldLibraryFolders(Hashtable AssetFolders, UUID parentUUID)
        {
            int itemCount;
            foreach (DictionaryEntry SubFolder in AssetFolders)
            {
                itemCount = 0;

                // Define folder
                InventoryFolderBase folder = new InventoryFolderBase();
                folder.Name = (string)SubFolder.Key;
                folder.ID = UUID.Random();
                folder.Owner = worldLibOwner;
                folder.ParentID = parentUUID;
                folder.Type = GetInventoryTypeFromName(folder.Name);
                folder.Version = 1;

                // Add folder to database, if exists break;
                InventoryDatabaseConnection.addInventoryFolder(folder);

                // Define inventory items
                List<AssetBase> folderItems = (List<AssetBase>)SubFolder.Value;
                InventoryItemBase folderItem;
                
                foreach (AssetBase item in folderItems)
                {
                    folderItem = new InventoryItemBase();
                    
                    UUID assetID;
                    if (UUID.TryParse(item.ID, out assetID))
                    {
                        folderItem.AssetID = assetID;
                        folderItem.ID = UUID.Random();
                        folderItem.Owner = worldLibOwner;
                        folderItem.CreatorId = worldLibOwner.ToString();
                        folderItem.Name = item.Name;
                        folderItem.Description = item.Description;
                        folderItem.AssetType = (int)item.Type;
                        folderItem.InvType = (int)CableBeachMessages.InventoryType.Object; // FIX
                        folderItem.Folder = parentUUID;
                        folderItem.BasePermissions = 0x7FFFFFFF;
                        folderItem.EveryOnePermissions = 0x7FFFFFFF;
                        folderItem.CurrentPermissions = 0x7FFFFFFF;
                        folderItem.NextPermissions = 0x7FFFFFFF;

                        InventoryDatabaseConnection.addInventoryItem(folderItem);
                        itemCount++;
                    }
                }
                Logger.InfoFormat("[WorldLibrary] Created folder {0} with {1} items", folder.Name, itemCount.ToString());
            }

        }

        private short GetInventoryTypeFromName(string typeName)
        {
            if (typeName == "Textures")
                return (short)CableBeachMessages.InventoryType.Texture;
            else if (typeName == "3D Meshes")
                return (short)CableBeachMessages.InventoryType.OgreMesh;
            else if (typeName == "3D Materials")
                return (short)CableBeachMessages.InventoryType.OgreMaterial;
            else if (typeName == "3D Particles")
                return (short)CableBeachMessages.InventoryType.OgrePartice;
            else if (typeName == "Sounds")
                return (short)CableBeachMessages.InventoryType.Sound;
            else if (typeName == "Scripts")
                return (short)CableBeachMessages.InventoryType.LSL;
            else if (typeName == "Gestures")
                return (short)CableBeachMessages.InventoryType.Gesture;
            else if (typeName == "Clothing")
                return (short)CableBeachMessages.InventoryType.Wearable;
            else if (typeName == "Bodyparts")
                return (short)13;
            else if (typeName == "Animations")
                return (short)CableBeachMessages.InventoryType.Animation;
            else
                return (short)CableBeachMessages.InventoryType.Object;
        }

        public bool MigrateAssetTables()
        {
            try
            {
                AssetDatabaseConnection.Initialise(assetConnectionString);  
                Logger.Info("[DatabaseMigration] Assets migration succesfull");
                return true;
            }
            catch (Exception ex)
            {
                Logger.ErrorFormat("[DatabaseMigration] Could not migrate assets database with connection string {0}", assetConnectionString);
                return false;
            }
        }
        
        public void LoadDefaultAssets() 
        {
            Logger.InfoFormat("[DatabaseMigration] Trying to load OpenSim default assets from {0}. This may take a while...", defaultAssetsFile);
            try
            {
                AssetLoaderFileSystem AssetLoader = new AssetLoaderFileSystem();
                AssetLoader.ForEachDefaultXmlAsset(defaultAssetsFile, StoreAsset);
                Logger.InfoFormat("[DatabaseMigration] Migrated default assets to database succesfully");
            }
            catch (Exception ex)
            {
                Logger.Error("[DatabaseMigration] Errors occurred while trying to load defaultAssets from filesystem");

                Logger.Error(ex.ToString());
            }
        }

        public void StoreAsset(AssetBase asset)
        {
            // Add to List for creating world library
            defaultAssets.Add(asset);

            UUID assetUUID = UUID.Zero;
            if (UUID.TryParse(asset.ID, out assetUUID))
            {
                if (!AssetDatabaseConnection.ExistsAsset(assetUUID))
                {
                    Logger.InfoFormat("[AssetLoader] Storing asset {0}", asset.Name);
                    AssetDatabaseConnection.StoreAsset(asset);
                }
            }
        }

    }
}
